package com.wlc.thread.jucDemo;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author 王立朝
 * @date 2022/4/6
 * @description: Volatile 关键字 是Java虚拟机提供的轻量级的 同步机制
 * 有3大特性：
 * 1、可见性
 * 2、不保证原子性
 * 3、禁止指令重排
 * 1、验证volatile的可见性
 * 1.1 初始值 int data =0; 不使用volatile关键字， 当一个线程修改完 变量data之后，其他线程无感知；
 * 1.2 使用volatile关键字可以解决线程之间的可见性
 * 2、验证volatile的原子性
 * 什么是原子性？
 * 原子性： 就是在一个线程执行的过程中，中间不可以被加塞活着被分割，必须整体完整，
 * 要么同时成功，要么同时失败
 * 解决方案：
 * 1、使用 synchronized 关键字（但是使用 synchronized太重了，不建议使用）
 * 2、使用JUC下的 AtomicInteger 可以进行原子更新的int值。
 */

class MyData {
    volatile int data = 0;

    public void addDataTo60() {
        this.data = 60;
    }

    /**
     * data++
     */
    public void addToAdd() {
        data++;
    }

    /**
     * 默认值为0
     */

    AtomicInteger atomicInteger = new AtomicInteger();

    public void addAtomicInteger() {
        // i++
        atomicInteger.incrementAndGet();
    }
}

/**
 * @author 王立朝
 */
public class VolatileDemo {


    public static void main(String[] args) {

        nonatomic();
    }

    /**
     * volatile 非原子性
     */
    public static void nonatomic() {
        // 定义20个 线程，然后每个线程调用addToAdd 方法，调用1000次，结果应该是 20000
        MyData myData = new MyData();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myData.addToAdd();
                    myData.addAtomicInteger();
                }
            }, String.valueOf(i)).start();
        }
        // 如果有大于2个线程的情况下，就让主线程礼让，知道只有2个线程之后，再继续执行
        while (Thread.activeCount() > 2) {

            Thread.yield();
        }
        System.out.println("最终的计算结果为：volatile " + myData.data);
        System.out.println("最终的计算结果为：atomicInteger 原子性 " + myData.atomicInteger);

    }

    /**
     * volatile 保证线程之间的可见性
     */
    public static void visibilityOk() {
        MyData myData = new MyData();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.addDataTo60();
            System.out.println(Thread.currentThread().getName() + "\t update value:" + myData.data);
        }, "AAA").start();

        while (myData.data == 0) {

        }
        System.out.println(Thread.currentThread().getName() + " \t 更新后的data的值为：" + myData.data);
    }
}


